/*
 *      Copyright (C) 2005-2011 Team XBMC
 *      http://www.xbmc.org
 *
 *  This Program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  This Program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with XBMC; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *  http://www.gnu.org/copyleft/gpl.html
 *
 */

#include "client.h"
#include "xbmc_pvr_dll.h"
#include "platform/util/util.h"
#include "lib/protocol.h"
#include "PVREtp.h"

using namespace std;
using namespace ADDON;

#ifdef TARGET_WINDOWS
#define snprintf _snprintf
#endif

#define DEFAULT_HOST	"media"
#define DEFAULT_PORT	9997

bool			m_bCreated       = false;
ADDON_STATUS	m_CurStatus      = ADDON_STATUS_UNKNOWN;
bool			m_bIsPlaying     = false;
PVREtp 			*m_data           = NULL;

/* User adjustable settings are saved here.
 * Default values are defined inside client.h
 * and exported to the other source files.
 */

std::string g_strUserPath             = "";
std::string g_strClientPath           = "";


std::string   g_szHostname              = DEFAULT_HOST;
int           g_iPort                   = DEFAULT_PORT;


CHelper_libXBMC_addon *XBMC			= NULL;
CHelper_libXBMC_codec *CODEC		= NULL;
CHelper_libXBMC_pvr   *PVR			= NULL;
CHelper_libXBMC_gui   *GUI			= NULL;

extern "C" {

ADDON_STATUS ADDON_Create(void* hdl, void* props)
{
	if (!hdl || !props)
		return ADDON_STATUS_UNKNOWN;

	PVR_PROPERTIES* pvrprops = (PVR_PROPERTIES*)props;

	XBMC = new CHelper_libXBMC_addon;

	if (!XBMC->RegisterMe(hdl))
	{
		SAFE_DELETE(XBMC);
		return ADDON_STATUS_PERMANENT_FAILURE;
	}

	GUI = new CHelper_libXBMC_gui;
	if (!GUI->RegisterMe(hdl))
	{
		SAFE_DELETE(GUI);
		SAFE_DELETE(XBMC);
		return ADDON_STATUS_PERMANENT_FAILURE;
	}

	CODEC = new CHelper_libXBMC_codec;
	if (!CODEC->RegisterMe(hdl))
 	{
		SAFE_DELETE(CODEC);
		SAFE_DELETE(GUI);
		SAFE_DELETE(XBMC);
		return ADDON_STATUS_PERMANENT_FAILURE;
	}

	PVR = new CHelper_libXBMC_pvr;
	if (!PVR->RegisterMe(hdl))
 	{
		SAFE_DELETE(PVR);
	    SAFE_DELETE(CODEC);
	    SAFE_DELETE(GUI);
		SAFE_DELETE(XBMC);
		return ADDON_STATUS_PERMANENT_FAILURE;
	}

	if (XBMC) XBMC->Log(LOG_DEBUG, "%s - Creating the EDPVR add-on", __FUNCTION__);

	m_CurStatus     = ADDON_STATUS_UNKNOWN;

	//char * buffer = (char*) calloc(1, 128);
	char *buffer = new char[256]();

	if (XBMC->GetSetting("host", buffer))
	{
		g_szHostname = buffer;
	}
	else
	{
		/* If setting is unknown fallback to defaults */
		//if (XBMC) XBMC->Log(LOG_ERROR, "Couldn't get 'host' setting, falling back to '%s' as default", DEFAULT_HOST);
		if (XBMC) XBMC->Log(LOG_DEBUG, "Couldn't get 'host' setting, falling back to '%s' as default", DEFAULT_HOST);
		g_szHostname = DEFAULT_HOST;
	}

//	if (g_szHostname != buffer)
//		delete buffer;

	/* Read setting "port" from settings.xml */
	if (!XBMC->GetSetting("port", &g_iPort))
	{
		/* If setting is unknown fallback to defaults */
		//if (XBMC) XBMC->Log(LOG_ERROR, "Couldn't get 'port' setting, falling back to '%i' as default", DEFAULT_PORT);
		if (XBMC) XBMC->Log(LOG_DEBUG, "Couldn't get 'port' setting, falling back to '%i' as default", DEFAULT_PORT);
		g_iPort = DEFAULT_PORT;
	}

	if (XBMC) XBMC->Log(LOG_DEBUG, "Creating PVREtp: %s:%d\n", g_szHostname.c_str(), g_iPort);

	if (!m_data)
		m_data = new PVREtp(g_szHostname, g_iPort);

	if (XBMC) XBMC->Log(LOG_DEBUG, "New done\n");

	if (m_data && m_data->Ping())
	{
		if (XBMC) XBMC->Log(LOG_DEBUG, "Backend IP/Port: %s:%d\n", g_szHostname.c_str(), g_iPort);
	}
	else
	{
		m_bCreated = false;
		if (XBMC) XBMC->Log(LOG_DEBUG, "BAD!\n");
		SAFE_DELETE(PVR);
	    SAFE_DELETE(CODEC);
	    SAFE_DELETE(GUI);
		SAFE_DELETE(XBMC);

		m_CurStatus = ADDON_STATUS_LOST_CONNECTION;
		return m_CurStatus;
	}

	int num;
	bool b;

	if (XBMC->GetSetting("mlbtv_adaptive", &b))
	{
		m_data->SetMLB_Adaptive(b);
	}

	if (XBMC->GetSetting("mlbtv_start_bitrate", &num))
	{
		m_data->SetMLB_StartBitrate(num);
	}
	g_strUserPath   = pvrprops->strUserPath;
	g_strClientPath = pvrprops->strClientPath;

	m_CurStatus = ADDON_STATUS_OK;
	m_bCreated = true;
	return m_CurStatus;
}

ADDON_STATUS ADDON_GetStatus()
{
	if (XBMC) XBMC->Log(LOG_DEBUG, "-- ADDON_GetStatus\n");
	return m_CurStatus;
}

void ADDON_Destroy()
{
	m_bCreated = false;

	if (m_data)
		SAFE_DELETE(m_data);
	m_data = NULL;

	SAFE_DELETE(CODEC);
	CODEC = NULL;

	SAFE_DELETE(PVR);
	PVR = NULL;

	SAFE_DELETE(GUI);
	GUI = NULL;

	SAFE_DELETE(XBMC);
	XBMC = NULL;

	m_CurStatus = ADDON_STATUS_UNKNOWN;
}

bool ADDON_HasSettings()
{
	return true;
}

unsigned int ADDON_GetSettings(ADDON_StructSetting ***sSet)
{
	if (sSet) {};
	return 0;
}

ADDON_STATUS ADDON_SetSetting(const char *settingName, const void *settingValue)
{
	string str = settingName;
	if (str == "host")
	{
		string tmp_sHostname;
 		if (XBMC) XBMC->Log(LOG_INFO, "Changed Setting 'host' from %s to %s", g_szHostname.c_str(), (const char*) settingValue);
		tmp_sHostname = g_szHostname;
		g_szHostname = (const char*) settingValue;
		if (tmp_sHostname != g_szHostname)
			return ADDON_STATUS_NEED_RESTART;
	}
	else if (str == "port")
	{
		if (XBMC) XBMC->Log(LOG_INFO, "Changed Setting 'port' from %u to %u", g_iPort, *(int*) settingValue);
		if (g_iPort != *(int*) settingValue)
		{
			g_iPort = *(int*) settingValue;
			return ADDON_STATUS_NEED_RESTART;
		}
	}
	else if (str == "mlbtv_adaptive")
	{
		if (m_data)
		{
			m_data->SetMLB_StartBitrate(*(int*)settingValue);
		}
	}
	else if (str == "mlbtv_start_bitrate")
	{
		if (m_data)
		{
			m_data->SetMLB_Adaptive(*(int*)settingValue);
		}
	}
	return ADDON_STATUS_OK;
}

void ADDON_FreeSettings()
{
}

/***********************************************************
 * PVR Client AddOn specific public library functions
 ***********************************************************/

const char* GetPVRAPIVersion(void)
{
	static const char *strApiVersion = XBMC_PVR_API_VERSION;
	return strApiVersion;
}

const char* GetMininumPVRAPIVersion(void)
{
	static const char *strMinApiVersion = XBMC_PVR_MIN_API_VERSION;
	return strMinApiVersion;
}

const char* GetGUIAPIVersion(void)
{
	static const char *strGuiApiVersion = XBMC_GUI_API_VERSION;
	return strGuiApiVersion;
}

const char* GetMininumGUIAPIVersion(void)
{
	static const char *strMinGuiApiVersion = XBMC_GUI_MIN_API_VERSION;
	return strMinGuiApiVersion;
}


PVR_ERROR GetAddonCapabilities(PVR_ADDON_CAPABILITIES* pCapabilities)
{
	pCapabilities->bSupportsTV              = true;
	pCapabilities->bSupportsRadio           = false;

	pCapabilities->bSupportsChannelGroups   = true;
	pCapabilities->bSupportsChannelScan     = false;

	pCapabilities->bSupportsEPG             = true;
	pCapabilities->bSupportsTimers          = true;
//	pCapabilities->bSupportsTimers          = false;
	pCapabilities->bHandlesInputStream      = true;
	pCapabilities->bSupportsRecordings      = true;
//	pCapabilities->bSupportsRecordingPlayCount   = true;
//	pCapabilities->bSupportsLastPlayedPosition   = true;

	pCapabilities->bHandlesDemuxing         = true;
//	pCapabilities->bHandlesDemuxing         = false;
	if (m_data)
		m_data->m_bDoDemux = pCapabilities->bHandlesDemuxing;
	return PVR_ERROR_NO_ERROR;
}

const char *GetBackendName(void)
{
	static const char *strBackendName = "edpvr backend";
	return strBackendName;
}

const char *GetBackendVersion(void)
{
	static CStdString strBackendVersion = "0.1";
	return strBackendVersion.c_str();
}

const char *GetConnectionString(void)
{
	static CStdString strConnectionString = "connected";
	return strConnectionString.c_str();
}

PVR_ERROR GetDriveSpace(long long *iTotal, long long *iUsed)
{
	*iTotal = 1024 * 1024 * 1024;
	*iUsed  = 0;
	return PVR_ERROR_NO_ERROR;
}

PVR_ERROR GetEPGForChannel(ADDON_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart, time_t iEnd)
{
//	if (XBMC) XBMC->Log(LOG_DEBUG, "--- GetEPGForChannel, id: %d num: %d\n", channel.iUniqueId, channel.iChannelNumber);
	if (m_data)
	    return m_data->GetEPGForChannel(handle, channel, iStart, iEnd);
	return PVR_ERROR_SERVER_ERROR;
}

int GetChannelsAmount(void)
{
	if (m_data)
		return m_data->GetChannelsAmount();
	return -1;
}

PVR_ERROR GetChannels(ADDON_HANDLE handle, bool bRadio)
{
	if (m_data)
		return m_data->GetChannels(handle, bRadio);
	return PVR_ERROR_SERVER_ERROR;
}

bool OpenLiveStream(const PVR_CHANNEL &channel)
{
//	if (XBMC) XBMC->Log(LOG_DEBUG, "OpenLiveStream (client)\n");
	if (m_data)
	{

		bool ret = m_data->OpenLiveStream(channel);
//		if (XBMC) XBMC->Log(LOG_DEBUG, "OpenLiveStream (client)2: %d\n", ret);
		return ret;
	}
	return false;
}

int ReadLiveStream(unsigned char *pBuffer, unsigned int iBufferSize)
{
//	if (XBMC) XBMC->Log(LOG_DEBUG, "ReadLiveStream (client)\n");
	if (m_data)
		return m_data->ReadLiveStream(pBuffer, iBufferSize);
	return 0;
}

long long SeekLiveStream(long long iPosition, int iWhence /* = SEEK_SET */)
{
	if (m_data)
		return m_data->SeekLiveStream(iPosition, iWhence);
	return -1;
}

long long PositionLiveStream(void)
{
//	if (XBMC) XBMC->Log(LOG_DEBUG, "PositionLiveStream (client)\n");
	if (m_data)
		return m_data->PositionLiveStream();
	return -1;
}

long long LengthLiveStream(void)
{
//	return -1;

//	if (XBMC) XBMC->Log(LOG_DEBUG, "LengthLiveStream (client)\n");
	if (m_data)
		return m_data->LengthLiveStream();
//	if (XBMC) XBMC->Log(LOG_DEBUG, "LengthLiveStream (sad)\n");
//	return PVR_ERROR_NOT_IMPLEMENTED;
	return -1;
}
const char * GetLiveStreamURL(const PVR_CHANNEL &channel)
{
	if (&channel)
	{
		if (XBMC) XBMC->Log(LOG_DEBUG, "GetLiveStreamURL (client)\n");
		return "";
	}
	return NULL;
}

void CloseLiveStream(void)
{
	if (m_data)
		m_data->CloseLiveStream();

//  m_bIsPlaying = false;
}

void PauseStream(bool bPaused)
{
//  (void)bPaused;
	if (m_data)
		m_data->PauseStream(bPaused);
}

bool CanPauseStream(void)
{
	if (m_data)
		return m_data->CanPauseStream();
	return false;
}

bool CanSeekStream(void)
{
	if (m_data)
		return m_data->CanSeekStream();
	return false;
}

int GetCurrentClientChannel(void)
{
	if (XBMC) XBMC->Log(LOG_DEBUG, "[CLI] GetCurrentClientChannel\n");
//	return m_currentChannel.iUniqueId;
	return -1;
}

bool SwitchChannel(const PVR_CHANNEL &channel)
{
	if (XBMC) XBMC->Log(LOG_DEBUG, "[CLI] SwitchChannel\n");
	CloseLiveStream();
	return OpenLiveStream(channel);
}

PVR_ERROR GetStreamProperties(PVR_STREAM_PROPERTIES* pProperties)
{
//	if (XBMC) XBMC->Log(LOG_DEBUG, "[CLI] GetStreamProperties\n");
//	(void)pProperties;
	printf("GetStreamProperties\n");
  	if (m_data)
    	return m_data->GetStreamProperties(pProperties);
	return PVR_ERROR_NOT_IMPLEMENTED;
}

int GetChannelGroupsAmount(void)
{
	printf("[client] GetChannelGroupsAmount\n");
	if (m_data)
		return m_data->GetChannelGroupsAmount();
	return PVR_ERROR_NOT_IMPLEMENTED;
}

PVR_ERROR GetChannelGroups(ADDON_HANDLE handle, bool bRadio)
{
	printf("[client] GetChannelGroup\n");
	if (m_data)
		return m_data->GetChannelGroups(handle, bRadio);
	return PVR_ERROR_SERVER_ERROR;
}

PVR_ERROR GetChannelGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP &group)
{
	if (m_data)
		return m_data->GetChannelGroupMembers(handle, group);
	return PVR_ERROR_SERVER_ERROR;
}

PVR_ERROR SignalStatus(PVR_SIGNAL_STATUS &signalStatus)
{

//&signalStatus	if (XBMC) XBMC->Log(LOG_DEBUG, "SignalStatus (client)\n");
	if (m_data)
	{
		return m_data->SignalStatus(signalStatus);
	}
	return PVR_ERROR_NOT_IMPLEMENTED;
//	snprintf(signalStatus.strAdapterName, sizeof(signalStatus.strAdapterName), "pvr demo adapter 1");
//	snprintf(signalStatus.strAdapterStatus, sizeof(signalStatus.strAdapterStatus), "OK");

//	return PVR_ERROR_NO_ERROR;
}

int GetRecordingsAmount(void)
{
	if (m_data)
		return m_data->GetRecordingsAmount();
	return -1;
}

PVR_ERROR GetRecordings(ADDON_HANDLE handle)
{
	if (m_data)
		return m_data->GetRecordings(handle);
	return PVR_ERROR_NOT_IMPLEMENTED;
}

bool SeekTime(int a, bool b, double*c)
{
//	if (XBMC) XBMC->Log(LOG_DEBUG, "*********** SeekTime, time: %d, backwards: %d, startpts: %0.2f\n", a,b,*c);
	if (m_data)
		return m_data->SeekTime(a,b,c);
	return false;
}

DemuxPacket* DemuxRead(void)
{
	if (m_data)
		return m_data->DemuxRead();

//	if (XBMC) XBMC->Log(LOG_DEBUG, "---------- DEMUX READ\n");
	return NULL;
}

void DemuxFlush(void)
{
	if (m_data)
		return m_data->DemuxFlush();
	if (XBMC) XBMC->Log(LOG_DEBUG, "---------- DEMUX FLUSH\n");
}

void DemuxReset(void)
{
	if (m_data)
		return m_data->DemuxReset();
	if (XBMC) XBMC->Log(LOG_DEBUG, "---------- DEMUX RESET\n");
}

void DemuxAbort(void)
{
	if (m_data)
		return m_data->DemuxAbort();
	if (XBMC) XBMC->Log(LOG_DEBUG, "---------- DEMUX ABORT\n");
}

bool OpenRecordedStream(const PVR_RECORDING &recording)
{
//	if (XBMC) XBMC->Log(LOG_DEBUG, "client OpenRecordedStream\n");
	if (m_data)
		return m_data->OpenRecordedStream(recording);
	return false;
}

void CloseRecordedStream(void)
{
	//if (XBMC) XBMC->Log(LOG_DEBUG, "client CloseRecordedStream\n");
	if (m_data)
		return m_data->CloseRecordedStream();
}

int ReadRecordedStream(unsigned char *pBuffer, unsigned int iBufferSize)
{
	if (XBMC) XBMC->Log(LOG_DEBUG, "client ReadRecordedStream\n");

	if (m_data)
		return m_data->ReadRecordedStream(pBuffer, iBufferSize);
	return 0;
}

long long SeekRecordedStream(long long iPosition, int iWhence /* = SEEK_SET */)
{
//	if (XBMC) XBMC->Log(LOG_DEBUG, "client SeekRecordedStream\n");

	if (m_data)
		return m_data->SeekRecordedStream(iPosition, iWhence);
	return 0;
}

long long PositionRecordedStream(void)
{
	if (XBMC) XBMC->Log(LOG_DEBUG, "client PosRecordedStream\n");

	if (m_data)
		return m_data->PositionRecordedStream();
	return -1;
}

long long LengthRecordedStream(void)
{
//	if (XBMC) XBMC->Log(LOG_DEBUG, "client LenRecordedStream\n");

	if (m_data)
		return m_data->LengthRecordedStream();
	return -1;
}

PVR_ERROR DeleteRecording(const PVR_RECORDING &recording)
{
	if (m_data)
		return m_data->DeleteRecording(recording);
	return PVR_ERROR_NOT_IMPLEMENTED;
}

int GetTimersAmount(void)
{
	if (m_data)
		return m_data->GetTimersAmount();
	return -1;
}

PVR_ERROR GetTimers(ADDON_HANDLE handle)
{
	if (m_data)
		return m_data->GetTimers(handle);
	return PVR_ERROR_NOT_IMPLEMENTED;
}

PVR_ERROR AddTimer(const PVR_TIMER &timer)
{
	if (m_data)
		return m_data->AddTimer(timer);
	return PVR_ERROR_NOT_IMPLEMENTED;
}

PVR_ERROR DeleteTimer(const PVR_TIMER &timer, bool bForceDelete)
{
	if (m_data)
		return m_data->DeleteTimer(timer, bForceDelete);
	return PVR_ERROR_NOT_IMPLEMENTED;
}

PVR_ERROR UpdateTimer(const PVR_TIMER &timer)
{
	if (m_data)
		return m_data->UpdateTimer(timer);
	return PVR_ERROR_NOT_IMPLEMENTED;
}

time_t GetPlayingTime(void)
{
	if (m_data)
		return m_data->GetPlayingTime();
	return 0;
}

time_t GetBufferTimeStart(void)
{
	if (m_data)
		return m_data->GetBufferTimeStart();
	return 0;
}

time_t GetBufferTimeEnd(void)
{
	if (m_data)
		return m_data->GetBufferTimeEnd();
	return 0;
}

void ADDON_Stop() {}
void ADDON_Announce(const char *flag, const char *sender, const char *message, const void *data) {}

/** UNUSED API FUNCTIONS */
PVR_ERROR DialogChannelScan(void) { return PVR_ERROR_NOT_IMPLEMENTED; }
PVR_ERROR CallMenuHook(const PVR_MENUHOOK& menuhook, const PVR_MENUHOOK_DATA &item) { return PVR_ERROR_NOT_IMPLEMENTED; }
PVR_ERROR DeleteChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; }
PVR_ERROR RenameChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; }
PVR_ERROR MoveChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; }
PVR_ERROR DialogChannelSettings(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; }
PVR_ERROR DialogAddChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; }
PVR_ERROR RenameRecording(const PVR_RECORDING &recording) { return PVR_ERROR_NOT_IMPLEMENTED; }
PVR_ERROR SetRecordingPlayCount(const PVR_RECORDING &recording, int count) { return PVR_ERROR_NOT_IMPLEMENTED; }
PVR_ERROR SetRecordingLastPlayedPosition(const PVR_RECORDING &recording, int lastplayedposition) { return PVR_ERROR_NOT_IMPLEMENTED; }
int GetRecordingLastPlayedPosition(const PVR_RECORDING &recording) { return -1; }
unsigned int GetChannelSwitchDelay(void) { return 0; }
void SetSpeed(int) {};
PVR_ERROR GetRecordingEdl(const PVR_RECORDING&, PVR_EDL_ENTRY[], int*) { return PVR_ERROR_NOT_IMPLEMENTED; };


}
